package repositories

import (
	"context"

	"gorm.io/gorm"

	"github.com/samaa/photosalesplus/backend/internal/models"
)

type ProductRepository interface {
	List(ctx context.Context, search string, published *bool, limit, offset int) ([]models.Product, int64, error)
	GetByID(ctx context.Context, id uint) (*models.Product, error)
	PricingByIDs(ctx context.Context, ids []uint) (map[uint]ProductPricing, error)
	Create(ctx context.Context, product *models.Product) error
	Update(ctx context.Context, product *models.Product, images []models.ProductImage) error
	UpdatePublishStatus(ctx context.Context, id uint, published bool) error
	Delete(ctx context.Context, id uint) error
	FindProductIDByAssetID(ctx context.Context, assetID uint) (uint, error)
}

type productRepository struct {
	db *gorm.DB
}

func NewProductRepository(db *gorm.DB) ProductRepository {
	return &productRepository{db: db}
}

type ProductPricing struct {
	ID          uint
	Title       string
	Price       float64
	IsPublished bool
}

func (r *productRepository) List(ctx context.Context, search string, published *bool, limit, offset int) ([]models.Product, int64, error) {
	var (
		products []models.Product
		total    int64
	)

	query := r.db.WithContext(ctx).Model(&models.Product{})

	if search != "" {
		query = query.Where("title LIKE ? OR type LIKE ?", "%"+search+"%", "%"+search+"%")
	}

	if published != nil {
		query = query.Where("is_published = ?", *published)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	if err := query.
		Preload("CoverAsset").
		Preload("Images", func(db *gorm.DB) *gorm.DB {
			return db.Order("sort_order ASC").Preload("Asset")
		}).
		Limit(limit).
		Offset(offset).
		Order("created_at DESC").
		Find(&products).Error; err != nil {
		return nil, 0, err
	}

	return products, total, nil
}

func (r *productRepository) GetByID(ctx context.Context, id uint) (*models.Product, error) {
	var product models.Product
	if err := r.db.WithContext(ctx).
		Preload("CoverAsset").
		Preload("Images", func(db *gorm.DB) *gorm.DB {
			return db.Order("sort_order ASC").Preload("Asset")
		}).
		First(&product, id).Error; err != nil {
		return nil, err
	}
	return &product, nil
}

func (r *productRepository) PricingByIDs(ctx context.Context, ids []uint) (map[uint]ProductPricing, error) {
	result := make(map[uint]ProductPricing, len(ids))
	if len(ids) == 0 {
		return result, nil
	}

	var rows []ProductPricing
	if err := r.db.WithContext(ctx).
		Model(&models.Product{}).
		Select("id, title, price, is_published").
		Where("id IN ?", ids).
		Find(&rows).Error; err != nil {
		return nil, err
	}

	for _, row := range rows {
		result[row.ID] = row
	}
	return result, nil
}

func (r *productRepository) Create(ctx context.Context, product *models.Product) error {
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := tx.Create(product).Error; err != nil {
			return err
		}
		if len(product.Images) > 0 {
			for i := range product.Images {
				product.Images[i].ProductID = product.ID
				// Ensure auto-increment PK is used
				product.Images[i].ID = 0
			}
			if err := tx.Create(&product.Images).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

func (r *productRepository) Update(ctx context.Context, product *models.Product, images []models.ProductImage) error {
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := tx.Model(&models.Product{}).
			Where("id = ?", product.ID).
			Updates(map[string]interface{}{
				"title":              product.Title,
				"type":               product.Type,
				"description":        product.Description,
				"price":              product.Price,
				"cover_image":        product.CoverImage,
				"cover_asset_id":     product.CoverAssetID,
				"free_preview_limit": product.FreePreviewLimit,
				"is_published":       product.IsPublished,
			}).Error; err != nil {
			return err
		}

		if err := tx.Where("product_id = ?", product.ID).Delete(&models.ProductImage{}).Error; err != nil {
			return err
		}

		if len(images) > 0 {
			for i := range images {
				images[i].ProductID = product.ID
				// reset ID to avoid duplicate PK on re-insert
				images[i].ID = 0
			}
			if err := tx.Create(&images).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

func (r *productRepository) UpdatePublishStatus(ctx context.Context, id uint, published bool) error {
	return r.db.WithContext(ctx).Model(&models.Product{}).
		Where("id = ?", id).
		Update("is_published", published).Error
}

func (r *productRepository) Delete(ctx context.Context, id uint) error {
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := tx.Where("product_id = ?", id).Delete(&models.ProductImage{}).Error; err != nil {
			return err
		}
		return tx.Delete(&models.Product{}, id).Error
	})
}

func (r *productRepository) FindProductIDByAssetID(ctx context.Context, assetID uint) (uint, error) {
	type row struct{ ProductID uint }
	var out row
	if err := r.db.WithContext(ctx).
		Table("product_images").
		Select("product_id").
		Where("asset_id = ?", assetID).
		Limit(1).
		Scan(&out).Error; err != nil {
		return 0, err
	}
	return out.ProductID, nil
}
